import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

/**
 * LTS.java
 * Long Term Schedule that Provides Three Different Scheduling Algorithms
 * @author Will
 */
public class LTS {
	public static enum method { FCFS, PRIORITY, SJF;}
	public static method m = Driver.METHOD;
	private static boolean firstRun = true;
	//TODO Must Add Paging Logic to all three Algorithms.
	public static void schedule() {
		switch(m){
			case FCFS:
				fcfs();
				break;
			case PRIORITY:
				priority();
				break;
			case SJF:
				sjf();
				break;
		}	
	}
	
	/**
	 * Shortest Job First Algorithm
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private static void sjf() {
		boolean success = true;
		if(Jobs.waiting.size() > 0){
			if(Jobs.waiting.size() > 1){
				Collections.sort(Jobs.waiting, new Comparator(){
					public int compare(Object object1, Object object2){
						Process p1 = (Process)object1;
						Process p2 = (Process)object2;
						if(p1.length < p2.length){
							return -1;
						}
						else if(p1.length > p2.length){
							return 1;
						}
						else
							return 0;
					}
				});
			}
			ArrayList<Process> temp = new ArrayList<Process>();
			for(Process p: Jobs.waiting)
				temp.add(p);
			for(Process p: temp)
				if(Driver.PAGING){
					success = PageSchedule(p);
				}					
				else
					success = MemoryManager.Write(p);

			if(!success){
				return;
			}
		}		
		if(success){								
			Collections.sort(Jobs.allJobs, new Comparator(){
				public int compare(Object object1, Object object2){
					Process p1 = (Process)object1;
					Process p2 = (Process)object2;
					if(p1.length < p2.length){
						return -1;
					}
					else if(p1.length > p2.length){
						return 1;
					}
					else
						return 0;
				}
			});
		}		
		for(Process p: Jobs.allJobs){
			if(p.state == Process.State.NEW){
				if(Driver.PAGING){
					success = PageSchedule(p);
				}					
				else
					success = MemoryManager.Write(p);
			}
			if(!success)
				return;			
		}
	}
	
	/**
	 * Priority Scheduling Algorithm
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private static void priority() {
		boolean success = true;
		if(Jobs.waiting.size() > 0){
			Collections.sort(Jobs.waiting, new Comparator(){
				public int compare(Object object1, Object object2){
					Process p1 = (Process)object1;
					Process p2 = (Process)object2;
					if(p1.priority < p2.priority){
						return -1;
					}
					else if(p1.priority > p2.priority){
						return 1;
					}
					else
						return 0;
				}
			});
		}
		ArrayList<Process> temp = new ArrayList<Process>();
		for(Process p: Jobs.waiting)
			temp.add(p);
		for(Process p: temp){		
			if(Driver.PAGING){
				success = PageSchedule(p);
			}					
			else
				success = MemoryManager.Write(p);
		}
		if(!success)
			return;
		if(success){					
			Collections.sort(Jobs.allJobs, new Comparator(){
				public int compare(Object object1, Object object2){
					Process p1 = (Process)object1;
					Process p2 = (Process)object2;
					if(p1.priority < p2.priority){
						return -1;
					}
					else if(p1.priority > p2.priority){
						return 1;
					}
					else
						return 0;				
				}
			});
		}
		//temp = new ArrayList<Process>();
		for(Process p: Jobs.allJobs){
			if(p.state == Process.State.NEW){
				if(Driver.PAGING){
					success = PageSchedule(p);
				}					
				else
					success = MemoryManager.Write(p);
			}
			if(!success)
				return;
		}		
	}
	
	/**
	 * First Come First Serve Algorithm
	 */
	private static void fcfs() {
		boolean success = true;
		ArrayList<Process> tempW = new ArrayList<Process>();
		for(Process p: Jobs.waiting)
			tempW.add(p);
		for(Process p: tempW){	//Since there may be jobs waiting, they came first and thus go first.
			if(Driver.PAGING){
				success = PageSchedule(p);
			}					
			else
				success = MemoryManager.Write(p);					
		}
		if(!success)
			return;		
		if(success){								//If We've made it here, any process that was WAITING was placed into ram. 
			for(Process p: Jobs.allJobs){
				if(p.state == Process.State.NEW){
					if(Driver.PAGING){
						success = PageSchedule(p);
					}					
					else
						success = MemoryManager.Write(p);
				}
				if(!success){
					firstRun = false;
					return;
				}
			}
			if(firstRun)
				firstRun = false;
		}
	}

	private static boolean PageSchedule(Process p) {
		int control = 0;
		boolean success = false;
		for(Page page: p.pageTable){							
			if(!page.present){
				if(firstRun)
					success = MemoryManager.AddPage(page, p);									
				else
					success = MemoryManager.Fault(page, p);								
				control++;
				if(control >= 4)
					break;
				if(!success){
					firstRun = false;
					break;
				}									
			}
		}
		if(success)
			Jobs.ready.add(p);
		return success;
		
	}
}
